package org.yxt.message.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * netty客户端
 *
 * @author yxt
 * <p>
 * Sep 21, 2017
 */
public class NettyClient {

    private static Logger logger = LoggerFactory.getLogger(NettyClient.class);

    /**
     * 粘包处理分割符 \r\n
     */
    public static final String DELIMITER_CHART = "\r\n";

    private static EventLoopGroup eventLoopGroup = null;
    private static Bootstrap bootstrap = null;
    public static ChannelFuture future = null;
    private Channel channel;

    /**
     * IP
     */
    private static final String host = "192.168.1.6";
    /**
     * 端口
     */
    private static final int port = 7788;

    private NettyClient() {

    }

    private static volatile NettyClient instance;

    /**
     * 3定义静态方法类，返回实例
     *
     * @return NettyClient
     */
    public static NettyClient getInstance() {
        if (null == instance) {
            // 同步对象防止多次创建
            synchronized (NettyClient.class) {
                if (null == instance) {
                    // 初始化NettyClient
                    instance = new NettyClient();
                }
            }
        }
        return instance;
    }

    /**
     * 创建配置
     */
    public void run() {
        if (null != eventLoopGroup) {
            eventLoopGroup.shutdownGracefully();
        }
        // 开启4棵重用线程
        eventLoopGroup = new NioEventLoopGroup(4);
        try {
            bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            // 不足设置长度也发数据
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {

                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    // 心跳 10 秒钟
                    // 该类的第一个参数是指定读操作空闲秒数，
                    // 第二个参数是指定写操作的空闲秒数，
                    // 第三个参数是指定读写空闲秒数，当有操作操作超出指定空闲秒数时，
                    // 便会触发UserEventTriggered事件。所以我们只需要在自己的handler中截获该事件，
                    // 然后发起相应的操作即可（比如说发起心跳操作）
                    pipeline.addLast(new IdleStateHandler(0, 10, 0, TimeUnit.SECONDS));
                    // DelimiterBasedFrameDecoder用来解决以特殊符号作为消息结束符的粘包问题
                    // 消息结束符的粘包处理 使用\r\n做结束符
                    ByteBuf delimiter = Unpooled.copiedBuffer(DELIMITER_CHART.getBytes());
                    pipeline.addLast("framer", new DelimiterBasedFrameDecoder(2048, delimiter));
                    // 字符串解码 和 编码
                    pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                    pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                    pipeline.addLast(new NettyClientSimpleHandler());

                }

            });
            doConnect();
            // 关闭之后不能发送消息
            // future.channel().closeFuture().sync();
        } catch (Exception e) {
            eventLoopGroup.shutdownGracefully();
            logger.error("client start netty expection {}", e.getMessage());
        }

    }

    /**
     * 连接服务
     */
    protected void doConnect() {
        if (channel != null && channel.isActive()) {
            return;
        }
        try {
            // 获取连接数据 不能使用同步 因为有返回listener
            future = bootstrap.connect(host, port);
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture futureListener) throws Exception {
                    if (futureListener.isSuccess()) {
                        channel = futureListener.channel();
                        logger.info("Connect to server successfully");
                    } else {
                        logger.error("Failed to connect to server, try connect after 10s");
                        futureListener.channel().eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                doConnect();
                            }

                        }, 10, TimeUnit.SECONDS);
                    }
                }
            });
        } catch (Exception e) {
            logger.error("client connection server expection {}", e.getMessage());
        }
    }

    /**
     * 关闭连接
     */
    public static void closeNettyConn() {
        try {
            if (null != future) {
                future.channel().closeFuture().sync();
            }
            if (null != eventLoopGroup) {
                eventLoopGroup.shutdownGracefully();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送消息
     *
     * @param sendContent
     */
    public int sendMsg(String sendContent) {
        if (null != future && null != sendContent) {
            // 加入粘包处理编码器定义 \r\n
            sendContent = sendContent.concat(DELIMITER_CHART);
            ByteBuf respuffer = Unpooled.buffer(sendContent.length());
            respuffer.writeBytes(sendContent.getBytes(CharsetUtil.UTF_8));
            future.channel().writeAndFlush(respuffer);
            // future.addListener(new GenericFutureListener() {
            //
            // public void operationComplete(Future future) throws Exception {
            // // logger.info("send data status {}", future.isSuccess());
            // }
            // });
        }
        return 0;
    }

    public static void main(String[] args) throws InterruptedException {
        // closeNettyConn();
        NettyClient.getInstance().run();
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            NettyClient.getInstance().sendMsg("send data  ".concat(String.valueOf(i)));
        }
    }
}
